package me.sdevil507.supports.global.handler;

import lombok.extern.slf4j.Slf4j;
import me.sdevil507.biz.common.sms.captcha.exceptions.CaptchaInvalidException;
import me.sdevil507.supports.result.ApiResultDTO;
import me.sdevil507.supports.result.ApiResultGenerator;
import me.sdevil507.supports.status.ApiStatusCode;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import java.util.Objects;

/**
 * 全局异常捕获统一处理
 *
 * @author sdevil507
 */
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 处理@valid注解标记参数验证错误异常
     *
     * @param ex 参数绑定异常
     * @return json类型提示
     */
    @ExceptionHandler(value = {MethodArgumentNotValidException.class, BindException.class})
    @ResponseBody
    public ApiResultDTO dealWithValidationError(Exception ex) {
        log.error("无法通过参数验证:", ex);
        BindingResult result;
        if (ex instanceof BindException) {
            result = ((BindException) ex).getBindingResult();
        } else if (ex instanceof MethodArgumentNotValidException) {
            result = ((MethodArgumentNotValidException) ex).getBindingResult();
        } else {
            return ApiResultGenerator.create(ApiStatusCode.ERROR.getCode(), ApiStatusCode.ERROR.getDescription());
        }
        return ApiResultGenerator.create
                (ApiStatusCode.PARAMS_VALID_FAIL.getCode(), Objects.requireNonNull(Objects.requireNonNull(result.getFieldError()).getDefaultMessage()));
    }

    /**
     * 处理Shiro UnauthorizedException未授权访问资源异常
     *
     * @param ex 未授权访问异常
     * @return 反馈消息
     */
    @ExceptionHandler({UnauthorizedException.class})
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public String dealWithUnauthenticatedException(UnauthorizedException ex) {
        log.error("未授权访问异常:", ex);
        return "forward:/error/401";
    }

    /**
     * 处理shiro登录(密码输入错误次数过多)异常
     *
     * @param ex 异常
     * @return 反馈
     */
    @ExceptionHandler(ExcessiveAttemptsException.class)
    @ResponseBody
    public ApiResultDTO dealWithExcessiveAttemptsException(ExcessiveAttemptsException ex) {
        log.error("密码输入错误次数过多:", ex);
        return ApiResultGenerator.create(ApiStatusCode.ACCOUNT_EXCESSIVE_ATTEMPTS.getCode(), ApiStatusCode.ACCOUNT_EXCESSIVE_ATTEMPTS.getDescription());
    }

    /**
     * 处理shiro登录(账户被锁定)异常
     *
     * @param ex 异常
     * @return 反馈
     */
    @ExceptionHandler(LockedAccountException.class)
    @ResponseBody
    public ApiResultDTO dealWithLockedAccountException(LockedAccountException ex) {
        log.error("账户被锁定异常:", ex);
        return ApiResultGenerator.create(ApiStatusCode.ACCOUNT_LOCKED.getCode(), ApiStatusCode.ACCOUNT_LOCKED.getDescription());
    }

    /**
     * 处理shiro登录(账户已达最大登录人数)异常
     *
     * @param ex 异常
     * @return 反馈
     */
    @ExceptionHandler(ConcurrentAccessException.class)
    @ResponseBody
    public ApiResultDTO dealWithBeyondMaximumNumberOfLoginException(ConcurrentAccessException ex) {
        log.error("账户已达最大登录人数异常:", ex);
        return ApiResultGenerator.create(ApiStatusCode.ACCOUNT_MAXIMUM_NUMBER.getCode(), ApiStatusCode.ACCOUNT_MAXIMUM_NUMBER.getDescription());
    }

    /**
     * 处理shiro登录(账户不存在)异常
     *
     * @param ex 异常
     * @return 反馈
     */
    @ExceptionHandler(UnknownAccountException.class)
    @ResponseBody
    public ApiResultDTO dealWithUnknownAccountException(UnknownAccountException ex) {
        log.error("账户不存在异常:", ex);
        return ApiResultGenerator.create(ApiStatusCode.ACCOUNT_INVALID.getCode(), ApiStatusCode.ACCOUNT_INVALID.getDescription());
    }

    /**
     * 处理shiro登录(密码错误)异常
     *
     * @param ex 异常
     * @return 反馈
     */
    @ExceptionHandler(IncorrectCredentialsException.class)
    @ResponseBody
    public ApiResultDTO dealWithIncorrectCredentialsException(IncorrectCredentialsException ex) {
        log.error("密码错误异常:", ex);
        return ApiResultGenerator.create(ApiStatusCode.ACCOUNT_INVALID.getCode(), ApiStatusCode.ACCOUNT_INVALID.getDescription());
    }

    /**
     * 处理验证码无效异常
     *
     * @param ex 异常
     * @return 反馈
     */
    @ExceptionHandler(CaptchaInvalidException.class)
    @ResponseBody
    public ApiResultDTO dealWithCaptchaInvalidException(CaptchaInvalidException ex) {
        log.error("验证码无效异常:", ex);
        return ApiResultGenerator.create(ApiStatusCode.CAPTCHA_INVALID.getCode(), ApiStatusCode.CAPTCHA_INVALID.getDescription());
    }
}
